/**
 * 放置时间轴的一些算法
 * */
import {
  DEFAULT_DURATION,
  MIN_WIDTH,
  SHAPE_DICT,
  TIME_SCALE_ARR,
  TIMELINE_CONFIG,
  TIMES_ARR,
  VTUBER_COUNT_TYPE,
  VTUBER_TYPE,
  Z_INDEX_OBJ,
} from './enums';
import timeLine from './TimeLine';
import { uniqueId } from 'lodash';
import { calcSpectrum } from '@veditor/components/timeLine/js/workerOptimize';
import { generateI18n } from '@veditor/utils/common';
import { generateUUID, secondFormat } from '@veditor/components/timeLine/js/tools';
const lang = generateI18n('timelineTrack');

const BASE_INDEX = Object.keys(Z_INDEX_OBJ).length; // 基础层次
// 用于计算当前页面可以渲染多少轴线,并且每一小轴线的力度是多少
export function calcLineCount(width, time) {
  const ci = TIME_SCALE_ARR.findIndex(item => item < time);
  const res = [];
  const remaindWidth = ((time % TIME_SCALE_ARR[ci]) / time) * width; // 9.4px 余数宽度
  const mainWidth = width - remaindWidth; // 800 -9.4 = 790.6;
  const times = Math.floor(time / TIME_SCALE_ARR[ci]); // 当前倍数 21
  let curWidth = mainWidth;
  let curTimes = times;
  for (let i = 0; i < TIME_SCALE_ARR.length; i++) {
    if (i >= ci) {
      if (i === ci) {
        curTimes = times;
      } else if (i > ci) {
        curTimes = TIMES_ARR[i - 1];
      }
      curWidth = curWidth / curTimes;
      res.push({ width: curWidth, index: i });
      if (curWidth < MIN_WIDTH) {
        res.pop();
        break;
      }
    }
  }
  const { width: scaleWidth, index: scaleIndex } = res[res.length - 1];
  const scaleTimes = Math.floor(width / scaleWidth) + 1; //绘制小竖线的次数,这里+1的原因是有一个格子只占了1px
  const scaleTime = TIME_SCALE_ARR[scaleIndex]; // 得到没一小格的时间
  const showScaleTime = TIME_SCALE_ARR[scaleIndex - 1]; // 得到显示刻度的时间
  // 根据小次数计算出显示时间的times
  const scaleTimeArr = [];
  for (let i = 0; i < scaleTimes; i++) {
    const ct = i * scaleTime; // 当前刻度代表的时间
    const obj = { index: i };
    if (ct % showScaleTime === 0) {
      obj.time = ct;
      obj.timeText = secondFormat(ct);
    }
    scaleTimeArr.push(obj);
  }
  // 这里还需要得到可以显示当前时间的数组
  return {
    width: scaleWidth,
    times: scaleTimes,
    timeScale: scaleTimeArr,
    marginRight: width - (scaleTimes - 1) * scaleWidth,
  };
}
// 封装解析数据用到的方法
const PARSE = {
  // 用于解析broadcast轴内容
  parseBroadcast(broadcast, videoDuration) {
    const { TextDrive, AudioDrive } = timeLine;
    const { textDrive, audioDrive } = Z_INDEX_OBJ;
    const { textDriver, audioDriver, mode, voice, useAIFinTemplate } = broadcast;
    const { vol } = voice;
    if (useAIFinTemplate) {
      return null;
    } else if (mode === 'text') {
      const { url, voiceTime, startTime = 0, voiceData } = textDriver;
      if (url && voiceTime && voiceTime.length) {
        return new TextDrive({
          id: uniqueId(),
          zIndex: textDrive,
          videoDuration,
          voiceData,
          voiceTime,
          mark: generateUUID(),
          baseStartTime: startTime,
          vol,
        });
      }
      return null;
    } else {
      const { url, duration, name, startTime = 0 } = audioDriver;
      if (url) {
        return new AudioDrive({
          id: uniqueId(),
          zIndex: audioDrive,
          videoDuration,
          duration,
          audioSrc: url,
          audioName: name,
          mark: generateUUID(),
          baseStartTime: startTime,
          vol,
        });
      }
      return null;
    }
  },
  // 多人数字人解析语音播报数据,解析文本驱动轴需要借助数字人组件,需要得到数字人封面和名称
  parseDoubleBroadcast(broadcast, videoDuration, vtuberComponent) {
    const { DoubleDrive } = timeLine;
    const { doubleDrive } = Z_INDEX_OBJ;
    const { multiVtuberA, multiVtuberB } = vtuberComponent.data || {};
    const res = [];
    // 解析成两个数组
    const driverList = [[], []];
    broadcast.forEach(item => {
      const { channel } = item;
      driverList[channel].push(this._pauseBroadcastData(item));
    });
    driverList.forEach((item, index) => {
      const data = index === 0 ? multiVtuberA : multiVtuberB;
      const { chineseName, englishName, avatarUrl } = data;
      res.push(
        new DoubleDrive({
          id: uniqueId(),
          channel: index,
          zIndex: doubleDrive,
          videoDuration,
          mark: generateUUID(),
          broadcastList: item,
          name: chineseName,
          avatarUrl,
        }),
      );
    });
    return res;
  },
  // 用于解析music数据返回对象
  parseMusic(music, videoDuration) {
    const { BgMusic } = timeLine;
    const { bgMusic } = Z_INDEX_OBJ;
    const { id, url, loop, duration, name, vol, componentDuration = null } = music;
    if (id && url) {
      // 代表有背景音乐
      return new BgMusic({
        id: uniqueId(),
        loop,
        zIndex: bgMusic,
        videoDuration,
        musicName: name,
        musicSrc: url,
        musicDuration: duration,
        duration: componentDuration,
        mark: generateUUID(),
        vol,
      });
    }
    return null;
  },
  // 用于解析当个元素的数据
  // eslint-disable-next-line sonarjs/cognitive-complexity,complexity,max-lines-per-function
  parseComponents(item, index, duration, useAIFinTemplate) {
    const {
      Vtuber,
      Sticker,
      Signature,
      Graphics,
      VideoType,
      Shape,
      Action,
      Background3d,
      Camera,
      AiFinTemplate,
      Chart,
      Emotion,
    } = timeLine;
    const { action, background3d, camera, emotion } = Z_INDEX_OBJ;
    const { name, mark, data, style } = item;
    const { selectable } = style;
    const { startTime, componentDuration, visible, src } = data || {};
    const allTime = !componentDuration; // 组件时间不存在默认allTime
    const comDuration = !componentDuration ? duration : componentDuration;
    const zIndex = BASE_INDEX + index;
    const timeLineArr = []; // 时间轴对象数组
    switch (name) {
      case 'common-vtuber': {
        const { emotionTimeLine = [], countType = 0 } = item.data;
        timeLineArr.push(
          new Vtuber({
            zIndex,
            id: uniqueId(),
            videoDuration: duration,
            startTime,
            duration: comDuration,
            vtuberName: item.data.name,
            vtuberSrc: src,
            mark,
            type: VTUBER_TYPE[name],
            visible,
            selectable,
            countType,
          }),
        );
        if (emotionTimeLine.length) {
          timeLineArr.push(
            new Emotion({
              zIndex: emotion,
              id: uniqueId(),
              videoDuration: duration,
              emotionTimeLine,
              mark: `common-emotion-${generateUUID()}`,
            }),
          );
        }
        break;
      }
      case 'common-did-vtuber': {
        const MAX_NAME = 20;
        const { name } = data;
        // 这里需要去除名称的.png
        const nameArr = name.split('.');
        if (nameArr.length >= Number('2')) {
          nameArr.pop();
        }
        const nameStr = nameArr.join('.').substr(0, MAX_NAME);
        timeLineArr.push(
          new Vtuber({
            zIndex,
            id: uniqueId(),
            videoDuration: duration,
            duration: comDuration,
            startTime,
            vtuberName: `${lang('photoAvatar')}：${nameStr}`,
            vtuberSrc: src,
            mark,
            type: VTUBER_TYPE[name],
            visible,
            selectable,
          }),
        );
        break;
      }
      case 'common-sticker': {
        timeLineArr.push(
          new Sticker({
            zIndex,
            id: uniqueId(),
            videoDuration: duration,
            stickersName: item.data.name,
            startTime,
            duration: comDuration,
            allTime,
            mark,
            visible,
            selectable,
          }),
        );
        break;
      }
      case 'common-text': {
        const signatureName = item.data.value;
        timeLineArr.push(
          new Signature({
            zIndex,
            id: uniqueId(),
            videoDuration: duration,
            signatureName,
            startTime,
            duration: comDuration,
            allTime,
            mark,
            visible,
            selectable,
          }),
        );
        break;
      }
      case 'common-image': {
        const { src: graphicsSrc } = item.data;
        timeLineArr.push(
          new Graphics({
            zIndex,
            id: uniqueId(),
            videoDuration: duration,
            graphicsSrc,
            startTime,
            duration: comDuration,
            allTime,
            mark,
            visible,
            selectable,
          }),
        );
        break;
      }
      case 'common-video': {
        const { src: videoSrc, duration: vd, cover: coverSrc, vol } = item.data;
        timeLineArr.push(
          new VideoType({
            zIndex,
            id: uniqueId(),
            videoDuration: duration,
            videoSrc,
            coverSrc,
            startTime,
            duration: comDuration,
            originDuration: vd || Number.MAX_VALUE,
            allTime,
            mark,
            vol,
            visible,
            selectable,
          }),
        );
        break;
      }
      case 'common-shape': {
        const { type } = item.data;
        timeLineArr.push(
          new Shape({
            zIndex,
            id: uniqueId(),
            videoDuration: duration,
            shapeName: SHAPE_DICT[type],
            startTime,
            duration: comDuration,
            allTime,
            mark,
            visible,
            selectable,
          }),
        );
        break;
      }
      case 'common-realistic-vtuber': {
        // 这里需要创建一个超写实虚拟人和
        const { actionTimeLine, bgTimeLine, cameraTimeLine, src } = item.data;
        timeLineArr.push(
          new Vtuber({
            zIndex,
            id: uniqueId(),
            videoDuration: duration,
            vtuberName: item.data.name,
            vtuberSrc: src,
            mark,
            type: VTUBER_TYPE[name],
            visible,
            selectable,
          }),
        );
        if (actionTimeLine.length) {
          timeLineArr.push(
            new Action({
              zIndex: action,
              id: uniqueId(),
              videoDuration: duration,
              actionTimeLine,
              mark: `common-action-${generateUUID()}`,
            }),
          );
        }
        if (bgTimeLine.length) {
          if (useAIFinTemplate) {
            // 3d背景素材做金融模板的话,只能有一个数据(后续可能支持多个),第一个数据是金融模板
            const {
              name: templateName,
              thumbnailUrl,
              url,
              backgroundType,
              duration: vd,
              startTime: st,
            } = bgTimeLine[0];
            const coverSrc = backgroundType === 'movie' ? thumbnailUrl : url;
            timeLineArr.push(
              new AiFinTemplate({
                zIndex,
                id: uniqueId(),
                videoDuration: duration,
                coverSrc,
                templateName,
                startTime: st,
                duration: vd,
                allTime: true,
                mark: `common-ai-fin-template-${generateUUID()}`,
              }),
            );
          } else {
            timeLineArr.push(
              new Background3d({
                zIndex: background3d,
                id: uniqueId(),
                videoDuration: duration,
                bgTimeLine,
                mark: `common-bg3d-${generateUUID()}`,
              }),
            );
          }
        }
        if (cameraTimeLine.length) {
          timeLineArr.push(
            new Camera({
              zIndex: camera,
              id: uniqueId(),
              videoDuration: duration,
              cameraTimeLine,
              mark: `common-camera-${generateUUID()}`,
            }),
          );
        }
        break;
      }
      case 'common-ai-fin-template': {
        const { name: templateName, src: coverSrc, duration: vd } = item.data;
        timeLineArr.push(
          new AiFinTemplate({
            zIndex,
            id: uniqueId(),
            videoDuration: duration,
            coverSrc,
            templateName,
            startTime,
            duration: vd,
            allTime,
            mark,
            selectable,
          }),
        );
        break;
      }
      case 'common-chart': {
        const { src: graphicsSrc } = item.data;
        timeLineArr.push(
          new Chart({
            zIndex,
            id: uniqueId(),
            videoDuration: duration,
            graphicsSrc,
            startTime,
            duration: comDuration,
            allTime,
            mark,
            visible,
            selectable,
          }),
        );
        break;
      }
      default:
        break;
    }
    return timeLineArr;
  },
  // 用于解析mode和数据,返回一个对象
  _pauseBroadcastData(broadcastItem) {
    const { startTime, mode, textDriver, audioDriver, content, key } = broadcastItem;
    if (mode === 'text') {
      const { voiceTime, duration, url } = textDriver;
      return {
        mode,
        startTime,
        duration,
        voiceTime,
        content,
        mark: key,
        url,
      };
    } else {
      const { url, duration, name } = audioDriver;
      return {
        mode,
        startTime,
        duration,
        url,
        name,
        mark: key,
      };
    }
  },
};
// 用于解析短视频数据的方法,返回一个对象的数组,总时长
export function handleParseConfig(sceneConfig) {
  const timeLineInfo = {
    timeLineArr: [], // 时间轴对象的数组
  };
  const { components, music, duration, broadcast, multiConfig } = sceneConfig;
  const { useAIFinTemplate } = broadcast;
  timeLineInfo.videoDuration = duration > 0 ? duration : DEFAULT_DURATION;
  const musicObj = PARSE.parseMusic(music, timeLineInfo.videoDuration);
  musicObj && timeLineInfo.timeLineArr.push(musicObj);
  // 这里是对双人数字人解析的特殊逻辑
  const vtuberComponent = components.find(item => item.name === 'common-vtuber');
  if (vtuberComponent && vtuberComponent.data?.countType === VTUBER_COUNT_TYPE.DOUBLE) {
    const broadcastList = PARSE.parseDoubleBroadcast(
      multiConfig.broadcasts,
      timeLineInfo.videoDuration,
      vtuberComponent,
    );
    timeLineInfo.timeLineArr.push(...broadcastList);
  } else {
    const broadcastObj = PARSE.parseBroadcast(broadcast, timeLineInfo.videoDuration);
    broadcastObj && timeLineInfo.timeLineArr.push(broadcastObj); // 这里第一次初始化必须要有对象才能push
  }
  components.forEach((item, index) => {
    timeLineInfo.timeLineArr.push(
      ...PARSE.parseComponents(item, index, timeLineInfo.videoDuration, useAIFinTemplate),
    );
  });
  // 用于对timeLineArr排序
  timeLineInfo.timeLineArr.sort((a, b) => b.zIndex - a.zIndex);
  return timeLineInfo;
}
function judgeDriverTypeChange(bc, nc) {
  const vtuberNameList = Object.keys(VTUBER_TYPE);
  const beforeVtuber = bc.find(item => vtuberNameList.includes(item.name));
  const afterVtuber = nc.find(item => vtuberNameList.includes(item.name));
  return {
    flag: JSON.stringify(beforeVtuber) !== JSON.stringify(afterVtuber),
    vtuberComponent: afterVtuber,
  };
}

// 这里用于计算出上一步操作和当前操作的变化
// eslint-disable-next-line sonarjs/cognitive-complexity,complexity
export function findDIff(beforeData, now) {
  const { SINGLE, DOUBLE } = VTUBER_COUNT_TYPE;
  const arr = [];
  const { key: bk, music: bm, components: bc, broadcast: bb, multiConfig: bmc } = beforeData;
  const { key: nk, music: nm, components: nc, broadcast: nb, multiConfig: nmc } = now;
  const bd = beforeData.duration <= 0 ? DEFAULT_DURATION : beforeData.duration;
  const nd = now.duration <= 0 ? DEFAULT_DURATION : now.duration;
  // 场景不一样
  if (bk !== nk) {
    arr.push({ messageType: 'all', data: now }); // 全部更新
    return arr;
  }
  // 时长不一样
  if (nd !== bd) {
    arr.push({ messageType: 'duration', data: { duration: nd } }); // 更新时长
  }
  // 背景音乐不一样
  if (JSON.stringify(bm) !== JSON.stringify(nm)) {
    const musicObj = PARSE.parseMusic(nm, nd);
    arr.push({ messageType: 'bgMusic', data: musicObj });
  }
  const { flag, vtuberComponent } = judgeDriverTypeChange(bc, nc);
  // 这里处理一下语音播放数据变化
  const isDataChange =
    JSON.stringify(bb) !== JSON.stringify(nb) || JSON.stringify(bmc) !== JSON.stringify(nmc);
  if (isDataChange || flag) {
    if (vtuberComponent && vtuberComponent.data?.countType === VTUBER_COUNT_TYPE.DOUBLE) {
      const broadcastList = PARSE.parseDoubleBroadcast(nmc.broadcasts, nd, vtuberComponent);
      arr.push({ messageType: 'broadcast', data: broadcastList, doubleDrive: DOUBLE });
    } else {
      const broadcastObj = PARSE.parseBroadcast(nb, nd);
      // arr后续更新对broadcastObj为null做了判断,这里直接传null|对象进去就可以
      arr.push({ messageType: 'broadcast', data: [broadcastObj], doubleDrive: SINGLE });
    }
  }
  // 处理单个组件数据不一样
  if (JSON.stringify(bc) !== JSON.stringify(nc)) {
    nc.forEach((item, index) => {
      const { mark } = item;
      const beforeIndex = bc.findIndex(item1 => item1.mark === mark);
      // 判断元素存不存在
      if (beforeIndex !== -1) {
        const beforeItem = bc.splice(beforeIndex, 1)[0]; // 删除这个元素,代表已经比较过了
        // 这里单独判断一下一样不一样
        const isSame = JSON.stringify(item) === JSON.stringify(beforeItem);
        if (!isSame) {
          const newArr = PARSE.parseComponents(item, index, nd, nb.useAIFinTemplate);
          arr.push({ messageType: 'put', data: { mark, arr: newArr } }); // 目前put逻辑和add逻辑一样
        }
      } else {
        const newArr = PARSE.parseComponents(item, index, nd, nb.useAIFinTemplate);
        arr.push({ messageType: 'add', data: { arr: newArr } });
      }
    });
    // 这里最后判断一下bc里面还有没有数据,有的话代表删除元素
    if (bc.length !== 0) {
      bc.forEach(item => {
        const { mark, name } = item;
        arr.push({ messageType: 'del', data: { mark, vtuberType: VTUBER_TYPE[name] } });
      });
    }
    // 这里是二次渲染的虚拟人动作有默认的层次无需考虑,后续如果有二级渲染的可自定义层次的需要修改
    const newZIndexArr = [];
    nc.forEach((item, index) => {
      const zIndex = BASE_INDEX + index;
      newZIndexArr.push({
        mark: item.mark,
        zIndex,
      });
    });
    arr.push({ messageType: 'zIndex', data: newZIndexArr });
  }
  return arr;
}
// 计算时间的显示力度
export function convertSeconds(seconds) {
  const hours = Math.floor(seconds / Number('3600'));
  const minutes = Math.floor((seconds % Number('3600')) / Number('60'));
  const sec = parseFloat((seconds % Number('60')).toFixed(1));
  let timeString = '';
  if (hours > 0) {
    timeString = `${hours.toFixed(1)}H`;
  } else if (minutes > 0) {
    timeString = `${minutes.toFixed(1)}M`;
  } else {
    timeString = `${sec.toFixed(1)}S`;
  }
  return timeString;
}
// 删除组件里面的不解析组件
export function deleteUnParse(components) {
  const newArr = [];
  components.forEach(item => {
    const { name } = item;
    const WHITE_LIST = ['common-subtitle'];
    if (WHITE_LIST.indexOf(name) === -1) {
      newArr.push(item);
    }
  });
  return newArr;
}
// 计算频谱图方法,musicSrc,audioArrayBuffer传一个就行
export function calcSpectrumWorker(width, maxHeight, musicSrc, audioArrayBuffer, renderWidth = 3) {
  return new Promise(resolve => {
    const worker = new Worker(calcSpectrum);
    worker.postMessage({
      width,
      maxHeight,
      musicSrc,
      audioArrayBuffer,
      renderWidth,
    });
    worker.onmessage = e => {
      resolve(e.data);
      worker.terminate();
    };
    worker.onerror = e => {
      console.log(`web worker绘制文本canvas报错${e}`);
      worker.terminate();
    };
  });
}
// 用于解析轴列表生成子轴数组
// eslint-disable-next-line sonarjs/cognitive-complexity
export function parseTimeLineArr(timeLineArr) {
  const fatherObj = {}; // 父元素数组映射对象
  const fatherReflectObj = {}; // 映射对象,当时子元素的时候,直接放数据,如果是父元素的话清除数据,代表这一轴不统计
  const arr = [];
  timeLineArr.forEach(item => {
    const { name, fatherName, childrenList } = item;
    if (fatherName) {
      // 判断之前有没有father[name]这个元素
      if (fatherObj[fatherName]) {
        fatherObj[fatherName].push(item);
      } else {
        fatherObj[fatherName] = [item];
        fatherReflectObj[fatherName] = 0;
      }
    } else if (childrenList.length) {
      arr.push(item);
      // 不存在创建一个新的数组
      if (!fatherObj[name]) {
        fatherObj[name] = [];
      }
      item.childrenElList = fatherObj[name]; // 修改指向
      fatherReflectObj[name] = 1; // 代表这一线有father
    } else {
      arr.push(item);
    }
  });
  // 这里判断一下fatherObj 不为空的属性,把其加到对应的位置
  for (const key in fatherReflectObj) {
    // father没有用过的
    if (!fatherReflectObj[key]) {
      arr.push(...fatherObj[key]);
    }
  }
  // 这里需要父元素里的childrenListEl更新每一个里的startTime
  arr.forEach(item => {
    const { childrenElList, startTime } = item;
    if (childrenElList.length) {
      childrenElList.forEach(item1 => {
        item1.fatherStartTime = startTime;
        item1.fatherEl = item;
      });
    }
  });
  arr.sort((a, b) => b.zIndex - a.zIndex); // 这里重新对数据根据zIndex排序
  return arr;
}
// 用于还原time数组
export function reductionTimeLineArr(timeLineArr) {
  const arr = [];
  timeLineArr.forEach(item => {
    const { childrenElList } = item;
    arr.push(item);
    if (childrenElList.length) {
      childrenElList.forEach(item1 => {
        arr.push(item1);
      });
      childrenElList.splice(0, childrenElList.length);
    }
  });
  return arr;
}
// 拖拽列表排序
// eslint-disable-next-line sonarjs/cognitive-complexity
export function dragItemSort(dragItemList, type, currentIndex) {
  const { length } = dragItemList;
  for (let i = 0; i < length - 1; i++) {
    let min = i;
    for (let j = i + 1; j < length; j++) {
      const item = dragItemList[j].lineStyle;
      const next = dragItemList[min].lineStyle;
      const { width: iw, left: il } = item;
      const { width: nw, left: nl } = next;
      // 需要多这四个数进行减少精度
      if (type === 1) {
        if (il - nl < 0 || (il - nl < 0.5 && j === currentIndex)) {
          min = j;
        }
      } else {
        if (il + iw - (nl + nw) < 0 || (il + iw - (nl + nw) < 0.5 && j !== currentIndex)) {
          min = j;
        }
      }
    }
    const s = dragItemList[i];
    dragItemList[i] = dragItemList[min];
    dragItemList[min] = s;
  }
}
// 获取背景音乐时长
export function getAudioDuration(abArr) {
  const TIMEOUT = 5000;
  let timer = 0;
  return new Promise(async (resolve, reject) => {
    const audioBlob = new Blob([abArr], { type: 'audio/wav' });
    const audioBlobUrl = URL.createObjectURL(audioBlob);
    const audioEl = new Audio(audioBlobUrl);
    audioEl.addEventListener('canplay', () => {
      resolve(audioEl.duration);
      URL.revokeObjectURL(audioBlobUrl);
      clearTimeout(timer);
    });
    timer = setTimeout(() => {
      reject('timeout');
    }, TIMEOUT);
  });
}
// 给定删除的index和数组长度,判断下一个index的位置
export function calcNewSelect(delIndex, currentSelect, length) {
  // 判断下一个元素存在不,存在的话
  if (delIndex === currentSelect) {
    // 下一个元素存在的话,不变,
    const hasNextEl = delIndex + 1 < length;
    const hasBeforeEl = delIndex - 1 >= 0;
    if (hasNextEl) {
      return delIndex;
    } else if (!hasNextEl && hasBeforeEl) {
      return delIndex - 1;
    } else {
      return -1;
    }
  }
}
// 用于根据扩展比例来得到可视区域的总时长
export function calcViewDuration(videoDuration) {
  return videoDuration * TIMELINE_CONFIG.extendRate;
}
// 用于计算allWidth的宽度,allWidth:场景所占的宽度
export function calcAllWidth(viewWidth) {
  return viewWidth / TIMELINE_CONFIG.extendRate;
}
// 无感更新算法
export function getScaleRate(beforeDuration, nowDuration, beforeScale) {
  const scale = (beforeDuration / nowDuration) * beforeScale;
  return scale > 1 ? 1 : scale;
}
